package com.company.project.configurer;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.company.project.core.Result;
import com.company.project.core.ResultCode;
import com.company.project.exception.AuthenticationException;
import com.company.project.exception.ServiceException;
import com.company.project.util.AuthenticationInterceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * WebConfigurer
 *
 * @blame Android Team
 */ /* WebConfigurer
 * @Description:    Spring MVC 配置
 * @Author:         brilliantzhao
 * @CreateDate:     2019/5/25 22:45
 * @UpdateUser:
 * @UpdateDate:     2019/5/25 22:45
 * @UpdateRemark:
 */
@Configuration
public class WebConfigurer implements WebMvcConfigurer {

    private final Logger logger = LoggerFactory.getLogger(WebConfigurer.class);

    @Value("${spring.profiles.active}")
    //当前激活的配置文件
    private String env;

    //  设置拦截器拦截内容和规则
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 拦截所有请求，通过判断是否有 @LoginRequired 注解 决定是否需要登录
        registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");
    }

    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }

    /*
     * @Description: 设置跨域
     * @Author: brilliantzhao
     * @CreateDate: 2019/6/3 16:02
     * @Param:
     * @Return:
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")//拦截所有的url
                .allowedOrigins("*")// 放行哪些原始域，比如"http://domain1.com,https://domain2.com"
                .allowCredentials(true)// 是否发送Cookie信息
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 放行哪些原始域(请求方式)
                .allowedHeaders("*");// 放行哪些原始域(头部信息)
    }

    /*
     * @Description: 把消息转换器从jackson换成fastjson,使用阿里 FastJson 作为JSON MessageConverter
     * @Author: brilliantzhao
     * @CreateDate: 2019/6/3 15:52
     * @Param: [converters]
     * @Return: void
     */
    @Bean
    public HttpMessageConverters customConverters() {
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        // 创建配置类
        FastJsonConfig config = new FastJsonConfig();
        /**
         * QuoteFieldNames———-输出key时是否使用双引号,默认为true
         * WriteMapNullValue——–是否输出值为null的字段,默认为false
         * WriteNullNumberAsZero—-数值字段如果为null,输出为0,而非null
         * WriteNullListAsEmpty—–List字段如果为null,输出为[],而非null
         * WriteNullStringAsEmpty—字符类型字段如果为null,输出为”“,而非null
         * WriteNullBooleanAsFalse–Boolean字段如果为null,输出为false,而非null
         */
        config.setSerializerFeatures(
                // 空集合返回[],不返回null
                SerializerFeature.WriteNullListAsEmpty,
                // 防止循环引用
                SerializerFeature.DisableCircularReferenceDetect
        );
        //此处是全局处理方式
        config.setDateFormat("yyyy-MM-dd HH:mm:ss");
        config.setCharset(Charset.forName("UTF-8"));
        fastConverter.setFastJsonConfig(config);
        //
        fastConverter.setDefaultCharset(Charset.forName("UTF-8"));
        //
        List<MediaType> supportedMediaTypes = new ArrayList<>();
        supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        fastConverter.setSupportedMediaTypes(supportedMediaTypes);
        //支持text 转string
        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
        return new HttpMessageConverters(fastConverter, stringHttpMessageConverter);
    }

    /*
     * @Description: 全局统一异常处理
     * @Author: brilliantzhao
     * @CreateDate: 2019/5/30 14:48
     * @Param:
     * @Return:
     */
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        exceptionResolvers.add(new HandlerExceptionResolver() {
            @Override
            public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
                Result result = new Result();
                if (e instanceof ServiceException) {
                    //业务失败的异常，如“账号或密码错误”
                    result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
                    logger.info(e.getMessage());
                } else if (e instanceof AuthenticationException) {
                    //用户登录校验失败，token失效
                    result.setCode(ResultCode.UNAUTHORIZED).setMessage(e.getMessage());
                    logger.info(e.getMessage());
                } else if (e instanceof NoHandlerFoundException) {
                    result.setCode(ResultCode.NOT_FOUND).setMessage("接口 [" + request.getRequestURI() + "] 不存在");
                } else if (e instanceof ServletException) {
                    result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
                } else {
                    result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误，请联系管理员");
                    String message;
                    if (handler instanceof HandlerMethod) {
                        HandlerMethod handlerMethod = (HandlerMethod) handler;
                        message = String.format("接口 [%s] 出现异常，方法：%s.%s，异常摘要：%s",
                                request.getRequestURI(),
                                handlerMethod.getBean().getClass().getName(),
                                handlerMethod.getMethod().getName(),
                                e.getMessage());
                    } else {
                        message = e.getMessage();
                    }
                    logger.error(message, e);
                }
                responseResult(response, result);
                return new ModelAndView();
            }

        });
    }

    /*
     * @Description:
     * @Author: brilliantzhao
     * @CreateDate: 2019/5/30 15:01
     * @Param:
     * @Return:
     */
    private void responseResult(HttpServletResponse response, Result result) {
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        response.setStatus(200);
        // 异常解决
        // Request processing failed; nested exception is java.lang.IllegalStateException: getOutputStream() has already been called for this response] with root cause
        PrintWriter out = null;
        try {
            out = response.getWriter();
            out.write(
                    JSON.toJSONString(result));
        } catch (IOException e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        } finally {
            if (out != null) {
                out.close();
            }
        }

    }

}
